package cn.intotw.rdcj.task.spiders.client;

import cn.intotw.rdcj.task.spiders.netty.SettableTask;
import cn.intotw.rdcj.task.spiders.netty.SpidersClientInboundHandler;
import cn.intotw.rdcj.task.spiders.netty.SpidersResultHandler;
import cn.intotw.rdcj.task.spiders.parser.BiqugeParser;
import cn.intotw.rdcj.task.spiders.util.HttpHeaderFactory;
import cn.intotw.rdcj.task.spiders.util.HttpRequestFactory;
import io.netty.bootstrap.Bootstrap;
import io.netty.buffer.Unpooled;
import io.netty.channel.Channel;
import io.netty.channel.ChannelFutureListener;
import io.netty.channel.ChannelOption;
import io.netty.channel.ChannelPipeline;
import io.netty.channel.EventLoopGroup;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.pool.ChannelHealthChecker;
import io.netty.channel.pool.ChannelPool;
import io.netty.channel.pool.ChannelPoolHandler;
import io.netty.channel.pool.FixedChannelPool;
import io.netty.channel.socket.nio.NioSocketChannel;
import io.netty.handler.codec.http.FullHttpRequest;
import io.netty.handler.codec.http.HttpClientCodec;
import io.netty.handler.codec.http.HttpContentDecompressor;
import io.netty.handler.codec.http.HttpObjectAggregator;
import io.netty.handler.logging.LoggingHandler;
import io.netty.handler.ssl.SslContext;
import io.netty.handler.ssl.SslContextBuilder;
import io.netty.handler.ssl.util.InsecureTrustManagerFactory;
import lombok.Getter;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;

import javax.net.ssl.SSLException;
import java.io.UnsupportedEncodingException;
import java.net.InetSocketAddress;
import java.net.URI;
import java.net.URISyntaxException;
import java.net.URLDecoder;
import java.nio.charset.StandardCharsets;
import java.util.Arrays;
import java.util.List;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;

/**
 * Created by Chenxiang
 *
 * @generator: IntelliJ IDEA
 * @description: Netty爬虫客户端
 * @project: rdcj
 * @package: cn.intotw.rdcj.commodity.spiders
 * @date: 2020年07月23日 14时43分
 */
@Slf4j
public class NettySpidersClient {
    private ChannelPool channelPool;
    private Bootstrap bootstrap;
    @Getter
    private String host;
    @Getter
    private int port;

    public NettySpidersClient(URI uri) {
        init(uri);
    }

    //SSLException dont have to process
    @SneakyThrows(SSLException.class)
    private void init(URI uri) {
        String scheme = uri.getScheme() == null ? "http" : uri.getScheme();
        host = uri.getHost() == null ? "127.0.0.1" : uri.getHost();
        port = uri.getPort();
        if (port == -1) {
            if ("http".equalsIgnoreCase(scheme)) {
                port = 80;
            } else if ("https".equalsIgnoreCase(scheme)) {
                port = 443;
            }
        }

        if (!"http".equalsIgnoreCase(scheme) && !"https".equalsIgnoreCase(scheme)) {
            log.error("Only HTTP(S) is supported.");
            return;
        }

        // Configure SSL context if necessary.
        final boolean ssl = "https".equalsIgnoreCase(scheme);
        final SslContext sslCtx;
        if (ssl) {

            sslCtx = SslContextBuilder.forClient()
                    .trustManager(InsecureTrustManagerFactory.INSTANCE).build();

        } else {
            sslCtx = null;
        }
        // Configure the client.
        EventLoopGroup eventLoopGroup = new NioEventLoopGroup();

        bootstrap = new Bootstrap();
        bootstrap.group(eventLoopGroup)
                .channel(NioSocketChannel.class)
                .option(ChannelOption.TCP_NODELAY, true)
                .remoteAddress(InetSocketAddress.createUnresolved(host, port));
        channelPool = new FixedChannelPool(bootstrap, new NettyChannelPoolHandler(sslCtx), ChannelHealthChecker.ACTIVE,
                FixedChannelPool.AcquireTimeoutAction.NEW, 5000, 10, Integer.MAX_VALUE);

    }

    public SettableTask<String> sendAndSync(FullHttpRequest httpContent){
        final SettableTask<String> responseFuture = new SettableTask<>();
        ChannelFutureListener connectionListener = future -> {
            if (future.isSuccess()) {
                Channel channel = future.channel();
                channel.pipeline().addLast(new SpidersResultHandler(responseFuture));
            } else {
                responseFuture.setExceptionResult(future.cause());
            }
        };
        try {
            Channel channel = channelPool.acquire().syncUninterruptibly().getNow();
            log.info("channel status:{}",channel.isActive());
            channel.writeAndFlush(httpContent).addListener(connectionListener);
        } catch (Exception e) {
            log.error("netty写入异常!", e);
        }
        return responseFuture;
    }
    @SneakyThrows(URISyntaxException.class)
    public static void main(String[] args){
        String url = "http://www.xbiquge.la/modules/article/waps.php";
        URI uri = new URI(url);
        NettySpidersClient nettySpidersClient = new NettySpidersClient(new URI(url));
        ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(1, 1, 1000L, TimeUnit.SECONDS, new LinkedBlockingQueue<>(10));

        List<String> stringList= Arrays.asList("searchkey=%E5%B8%9D%E9%9C%B8\n");
        stringList.forEach(e->{
            threadPoolExecutor.execute(()->{
                try {
                    long currentTimeMillis = System.currentTimeMillis();
                    log.info("[TestForThread]Thread:{},begin,searchFor{}",Thread.currentThread().getId(), URLDecoder.decode(e, StandardCharsets.UTF_8.displayName()));
                    FullHttpRequest request = HttpRequestFactory.newFullPostHttpRequest(uri.getRawPath(), e);
                    request.headers().set(HttpHeaderFactory.getBiqugeHeaders(e.length()));
                    SettableTask<String> stringSettableTask = nettySpidersClient.sendAndSync(request);
                    String s = stringSettableTask.get();
                    log.info(s);
                    log.info("[TestForThread]Thread:{},end,cose:{}",Thread.currentThread().getId(),System.currentTimeMillis()-currentTimeMillis);
                } catch (InterruptedException | ExecutionException | UnsupportedEncodingException e1) {
                    log.error("",e1);
                }
            });
        });
    }
}
